package databaseVersionControl.infra.script;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import databaseVersionControl.domain.control.AbstractScriptKey;
import databaseVersionControl.domain.control.ScriptKey;
import databaseVersionControl.domain.db.DBComponent;
import databaseVersionControl.domain.script.ExecutableScript;
import databaseVersionControl.domain.script.MyScriptExample;
import databaseVersionControl.infra.annotation.ScriptOrder;
import databaseVersionControl.infra.annotation.ScriptSystem;
import databaseVersionControl.infra.exception.DVCNoScriptClassException;
import databaseVersionControl.infra.script.validation.ScriptValidation;

public class ScriptRegister {

	public static void register(Class<?> clazz) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
		if (!ScriptValidation.isScriptClass(clazz))
			throw new DVCNoScriptClassException(
					String.format(
									"Invalid script class %s. Has not annotation @DatabaseScript or any method with @ScriptOrder and @ScriptAuthor.",
									clazz.getSimpleName()));
		
		registerAllElementsFor(clazz);
	}
	
	public static DBComponent register(AbstractScriptKey scriptKey, ExecutableScript executableScript) {
		return Repository.getInstance().register(scriptKey, executableScript);
	}

	private static void registerAllElementsFor(Class<?> clazz) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
		for (Method method : getAllScriptMethods(clazz)) {
			AbstractScriptKey scriptKey = new ScriptKey(clazz, method.getAnnotation(ScriptOrder.class).value());
			
			if (method.getAnnotation(ScriptSystem.class) != null) scriptKey.setSystem(true);

			ExecutableScript executableScript = (ExecutableScript)invokeMethod(method, clazz);
			
			register(scriptKey, executableScript);
		}
	}

	private static List<Method> getAllScriptMethods(Class<?> clazz) throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException, InstantiationException {

		Method[] allMethods = clazz.getDeclaredMethods();

		List<Method> scriptMethods = new ArrayList<Method>();

		for (Method method : allMethods)
			if (ScriptValidation.isScriptMethod(method)) scriptMethods.add(method);
		
		return scriptMethods;
	}

	private static Object invokeMethod(Method method, Class<?> clazz) throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException, InstantiationException {
		return method.invoke(clazz.newInstance(), new Object[] {});
	}


	public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException, InstantiationException {
		register(MyScriptExample.class);
	}

}